package com.ruyuan.payment.server.wxpay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.gson.JsonObject;
import com.ruyuan.payment.server.exception.BusinessException;
import com.ruyuan.payment.server.exception.BusinessExceptionEnum;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;

@Service
public class WxPayService {

    private static final Logger LOG = LoggerFactory.getLogger(WxPayService.class);

    @Resource
    private WxPayProperties wxPayProperties;

    /**
     * 调用微信下单接口成功，结果：{"code_url":"weixin://wxpay/bizpayurl?pr=er78WWazz"}
     * @param payNo
     * @param desc
     * @param amount
     * @return
     */
    public String pay(String desc, String payNo, BigDecimal amount) {
        try {
            HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native");
            JsonObject jsonObject = new JsonObject();
            jsonObject.addProperty("mchid", wxPayProperties.getMerchantId());
            jsonObject.addProperty("notify_url", wxPayProperties.getNotifyUrl());
            jsonObject.addProperty("out_trade_no", payNo);
            jsonObject.addProperty("appid", wxPayProperties.getAppId());
            jsonObject.addProperty("description", desc);
            JsonObject jsonObjectAmount = new JsonObject();
            jsonObjectAmount.addProperty("total", amount.multiply(new BigDecimal("100")).intValue());
            jsonObjectAmount.addProperty("currency", "CNY");
            jsonObject.add("amount", jsonObjectAmount);
            String data = jsonObject.toString();
            LOG.info("调用微信下单接口开始，参数：{}", data);

            StringEntity entity = new StringEntity(data,"utf-8");
            entity.setContentType("application/json");
            httpPost.setEntity(entity);
            httpPost.setHeader("Accept", "application/json");

            //完成签名并执行请求
            CloseableHttpClient httpClient = getHttpClient(true);
            CloseableHttpResponse response = httpClient.execute(httpPost);

            try {
                int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode == 200) { //处理成功
                    String result = EntityUtils.toString(response.getEntity());
                    LOG.info("调用微信下单接口成功，结果：{}", result);
                    JSONObject bodyJson = JSON.parseObject(result);
                    return bodyJson.getString("code_url");
                } else {
                    LOG.warn("调用微信下单接口失败，原因：{}", JSON.toJSONString(response));
                    throw new BusinessException(BusinessExceptionEnum.WXPAY_ERROR);
                }
            } finally {
                response.close();
            }
        } catch (Exception e) {
            LOG.error("调用微信下单接口异常，原因：", e);
            throw new BusinessException(BusinessExceptionEnum.WXPAY_ERROR);
        }
    }

    public String query(String outTradeNo) {
        try {
            String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + outTradeNo + "?mchid=" + wxPayProperties.getMerchantId();
            LOG.info("调用微信订单查询接口开始，地址：{}", url);
            URIBuilder uriBuilder = new URIBuilder(url);
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            httpGet.addHeader("Accept", "application/json");

            //完成签名并执行请求
            CloseableHttpClient httpClient = getHttpClient(true);
            CloseableHttpResponse response = httpClient.execute(httpGet);

            try {
                int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode == 200) { //处理成功
                    String result = EntityUtils.toString(response.getEntity());
                    LOG.info("调用微信订单查询接口成功，结果：{}", result);
                    return result;
                } else {
                    LOG.warn("调用微信订单查询接口失败，原因：{}", JSON.toJSONString(response));
                    throw new BusinessException(BusinessExceptionEnum.WXPAY_ERROR);
                }
            } finally {
                response.close();
            }
        } catch (Exception e) {
            LOG.error("调用微信订单查询接口异常，原因：", e);
            throw new BusinessException(BusinessExceptionEnum.WXPAY_ERROR);
        }
    }

    public void close(String outTradeNo) {
        try {
            String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + outTradeNo + "/close";
            LOG.info("调用微信订单关闭接口开始，地址：{}", url);
            HttpPost httpPost = new HttpPost(url);
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type","application/json; charset=utf-8");

            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectMapper objectMapper = new ObjectMapper();

            ObjectNode rootNode = objectMapper.createObjectNode();
            rootNode.put("mchid",wxPayProperties.getMerchantId());

            objectMapper.writeValue(bos, rootNode);

            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));

            //完成签名并执行请求
            CloseableHttpClient httpClient = getHttpClient(true);
            CloseableHttpResponse response = httpClient.execute(httpPost);

            try {
                int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode == 200 || statusCode == 204) { //处理成功
                    LOG.info("调用微信订单关闭接口成功");
                } else {
                    LOG.warn("调用微信订单关闭接口失败，原因：{}", JSON.toJSONString(response));
                    throw new BusinessException(BusinessExceptionEnum.WXPAY_ERROR);
                }
            } finally {
                response.close();
            }
        } catch (Exception e) {
            LOG.error("调用微信订单关闭接口异常，原因：", e);
            throw new BusinessException(BusinessExceptionEnum.WXPAY_ERROR);
        }
    }

    public boolean refundPart(String outTradeNo, BigDecimal refundAmount, String refundRequestNo, BigDecimal channelAmount) {
        try {
            HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds");
            JsonObject jsonObject = new JsonObject();
            jsonObject.addProperty("out_trade_no", outTradeNo);
            jsonObject.addProperty("out_refund_no", refundRequestNo);
            JsonObject jsonObjectAmount = new JsonObject();
            jsonObjectAmount.addProperty("total", channelAmount.multiply(new BigDecimal("100")).intValue());
            jsonObjectAmount.addProperty("refund", refundAmount.multiply(new BigDecimal("100")).intValue());
            jsonObjectAmount.addProperty("currency", "CNY");
            jsonObject.add("amount", jsonObjectAmount);
            String data = jsonObject.toString();
            LOG.info("调用微信退款接口开始，参数：{}", data);

            StringEntity entity = new StringEntity(data,"utf-8");
            entity.setContentType("application/json");
            httpPost.setEntity(entity);
            httpPost.setHeader("Accept", "application/json");

            //完成签名并执行请求
            CloseableHttpClient httpClient = getHttpClient(true);
            CloseableHttpResponse response = httpClient.execute(httpPost);

            try {
                int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode == 200) { //处理成功
                    String result = EntityUtils.toString(response.getEntity());
                    LOG.info("调用微信退款接口成功，结果：{}", result);
                    JSONObject bodyJson = JSON.parseObject(result);
                    return true;
                } else {
                    LOG.warn("调用微信退款接口失败，原因：{}", JSON.toJSONString(response));
                    return false;
                }
            } finally {
                response.close();
            }
        } catch (Exception e) {
            LOG.error("调用微信退款接口异常，原因：", e);
            throw new BusinessException(BusinessExceptionEnum.WXPAY_ERROR);
        }
    }

    public String queryRefund(String refundNo) {
        try {
            String url = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/" + refundNo;
            LOG.info("调用微信退款查询接口开始，地址：{}", url);
            URIBuilder uriBuilder = new URIBuilder(url);
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            httpGet.addHeader("Accept", "application/json");

            //完成签名并执行请求
            CloseableHttpClient httpClient = getHttpClient(true);
            CloseableHttpResponse response = httpClient.execute(httpGet);

            try {
                int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode == 200) { //处理成功
                    String result = EntityUtils.toString(response.getEntity());
                    LOG.info("调用微信退款查询接口成功，结果：{}", result);
                    return result;
                } else {
                    LOG.warn("调用微信退款查询接口失败，原因：{}", JSON.toJSONString(response));
                    throw new BusinessException(BusinessExceptionEnum.WXPAY_ERROR);
                }
            } finally {
                response.close();
            }
        } catch (Exception e) {
            LOG.error("调用微信退款查询接口异常，原因：", e);
            throw new BusinessException(BusinessExceptionEnum.WXPAY_ERROR);
        }
    }

    public String tradeBill(String billDate) {
        try {
            String url = "https://api.mch.weixin.qq.com/v3/bill/tradebill?bill_date=" + billDate;
            LOG.info("调用微信申请交易账单接口开始，地址：{}", url);
            URIBuilder uriBuilder = new URIBuilder(url);
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            httpGet.addHeader("Accept", "application/json");

            //完成签名并执行请求
            CloseableHttpClient httpClient = getHttpClient(true);
            CloseableHttpResponse response = httpClient.execute(httpGet);

            try {
                int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode == 200) { //处理成功
                    String result = EntityUtils.toString(response.getEntity());
                    LOG.info("调用微信申请交易账单接口成功，结果：{}", result);
                    return result;
                } else {
                    LOG.warn("调用微信申请交易账单接口失败，原因：{}", JSON.toJSONString(response));
                    throw new BusinessException(BusinessExceptionEnum.WXPAY_ERROR);
                }
            } finally {
                response.close();
            }
        } catch (Exception e) {
            LOG.error("调用微信申请交易账单接口异常，原因：", e);
            throw new BusinessException(BusinessExceptionEnum.WXPAY_ERROR);
        }
    }

    public String[][] downloadBill(String downloadUrl) {
        try {
            LOG.info("调用微信下载账单接口开始，地址：{}", downloadUrl);
            URIBuilder uriBuilder = new URIBuilder(downloadUrl);
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            httpGet.addHeader("Accept", "application/json");

            //完成签名并执行请求
            CloseableHttpClient httpClient = getHttpClient(false);
            CloseableHttpResponse response = httpClient.execute(httpGet);

            try {
                int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode == 200) { //处理成功
                    String result = EntityUtils.toString(response.getEntity());
                    LOG.info("调用微信下载账单接口成功，结果：{}", result);
                    return readBillData(result);
                } else {
                    LOG.warn("调用微信下载账单接口失败，原因：{}", JSON.toJSONString(response));
                    throw new BusinessException(BusinessExceptionEnum.WXPAY_ERROR);
                }
            } finally {
                response.close();
            }
        } catch (Exception e) {
            LOG.error("调用微信下载账单接口异常，原因：", e);
            throw new BusinessException(BusinessExceptionEnum.WXPAY_ERROR);
        }
    }

    private CloseableHttpClient getHttpClient(boolean isVerifier) throws Exception {
        // 示例：私钥为String字符串
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
                new ByteArrayInputStream(wxPayProperties.getMerchantPrivateKey().getBytes("utf-8")));

        // 获取证书管理器实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        // 向证书管理器增加需要自动更新平台证书的商户信息
        certificatesManager.putMerchant(wxPayProperties.getMerchantId(), new WechatPay2Credentials(wxPayProperties.getMerchantId(),
                new PrivateKeySigner(wxPayProperties.getMerchantSerialNumber(), merchantPrivateKey)), wxPayProperties.getApiV3Key().getBytes(StandardCharsets.UTF_8));
        // ... 若有多个商户号，可继续调用putMerchant添加商户信息

        // 从证书管理器中获取verifier
        Verifier verifier = certificatesManager.getVerifier(wxPayProperties.getMerchantId());
        WechatPayHttpClientBuilder builder;
        if (isVerifier) {
            builder = WechatPayHttpClientBuilder.create()
                    .withMerchant(wxPayProperties.getMerchantId(), wxPayProperties.getMerchantSerialNumber(), merchantPrivateKey)
                    .withValidator(new WechatPay2Validator(verifier));
        } else {
            builder = WechatPayHttpClientBuilder.create()
                    .withMerchant(wxPayProperties.getMerchantId(), wxPayProperties.getMerchantSerialNumber(), merchantPrivateKey)
                    .withValidator(response -> true);
        }

        LOG.info("定时更新平台证书：{}", JSON.toJSONString(verifier));

        // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签，并进行证书自动更新
        CloseableHttpClient httpClient = builder.build();
        return httpClient;
    }

    /**
     * 解析微信账单数据
     */
    private String[][] readBillData(String billData) {
        String[] rows = billData.split("\r\n");
        String[][] result = new String[rows.length-3][]; // 注意要去掉头尾三行
        // 第一行是表头，最后两行是汇总
        for (int i = 1; i < rows.length - 2; i++) {
            String row = rows[i].replace("`", "");
            String[] cells = row.split(",");
            LOG.info("第{}行：{}", i, cells);
            result[i - 1] = cells;
            LOG.info("数组数据：{}", JSON.toJSONString(result[i - 1]));
        }
        LOG.info("二维数组：{}", JSON.toJSONString(result));
        return result;
    }
}
